#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <memory>
#include <unordered_map>
#include "Epoller.hpp"
#include "TcpServerSocket.hpp"
#include "Connection.hpp"
#include "./Protocol/Protocol.hpp"
#include "./ProtocolFactory.hpp"
#include "Secure.hpp"
#include "Callback.hpp"

class InetworkModule
{
public:
    using Ptr = std::shared_ptr<InetworkModule>;

    using connectedCallback = std::function<void(TcpConnectPtr)>;
    using disconnectedCallback = std::function<void(TcpConnectPtr)>;
    using unknownCallback = std::function<void(TcpConnectPtr)>;

    // using Callback = std::function<std::string(ProtocolPtr)>;

    InetworkModule(uint16_t port) : _running(false), _epoller(std::make_unique<Epoller>())
    {
        _epoller->Create();
        TcpServerSocket listensock;
        listensock.Init(port);
        uint32_t event = (EPOLLIN | EPOLLET);
        SetNonBlock(listensock.GetFd());
        TcpConnectPtr listen = Factory::BuildListenConn(listensock.GetFd(), event, std::bind(&InetworkModule::Accept, this, std::placeholders::_1), this);

        AddConnection(listen, event);
    }

    template <typename T>
    void registerCallback(Protocol::Type type, const typename CallbackT<T>::ProtocolTCallback &callback)
    {
        std::shared_ptr<CallbackT<T>> pd(new CallbackT<T>(callback));
        callbacks.insert(std::make_pair(type, pd));
    }

    void onUnknownCallback(unknownCallback &&cb)
    {
        unknown_callback = std::move(cb);
    }

    void onConnect(connectedCallback &&cb)
    {
        conn_callback = std::move(cb);
    }

    void onDisConnect(disconnectedCallback &&cb)
    {
        disconn_callback = std::move(cb);
    }

    void Start()
    {
        _running = true;
        while (_running)
        {
            LoopOnce();
            // _epoller->PrintBug();
            sleep(1);
        }
        _running = false;
    }

    void Send(const TcpConnectPtr &conn, const ProtocolPtr &message)
    {
        std::string result = message->serialize();
        // 封装响应报文
        Package(result, &result);
        // 放到发送缓冲区中
        for (auto ch : result)
        {
            conn->GetOutbuffer().push_back(ch);
        }
        Writer(conn);
    }

    void Close(const TcpConnectPtr &conn)
    {
        Exceptor(conn);
    }

    virtual ~InetworkModule() {}

    // void Init()
    // {
    // }
private:
    bool ExistFd(int fd)
    {
        return !(_connections.find(fd) == _connections.end());
    }

    void AddConnection(TcpConnectPtr conn, uint32_t event)
    {
        if (conn == nullptr)
            return;
        if (ExistFd(conn->GetFd()))
            return;

        _connections[conn->GetFd()] = conn;
        _epoller->Add(conn->GetFd(), event);
        ILOG("添加新连接对象成功：%d\n", conn->GetFd());
    }

    void ModConnection(TcpConnectPtr conn, bool is_reader, bool is_writer)
    {
        if (conn == nullptr)
            return;
        if (!ExistFd(conn->GetFd()))
            return;

        uint32_t event = (is_reader ? EPOLLIN : 0) | (is_writer ? EPOLLOUT : 0) | EPOLLET;
        _connections[conn->GetFd()]->SetEvent(event);
        _epoller->Mod(conn->GetFd(), event);
        // ILOG("修改新连接对象成功：%d\n", conn->GetFd());
    }

    void DelConnection(TcpConnectPtr conn)
    {
        if (conn == nullptr)
            return;
        if (!ExistFd(conn->GetFd()))
            return;

        _connections.erase(conn->GetFd());
        _epoller->Del(conn->GetFd());
        // ILOG("删除新连接对象成功：%d", conn->GetFd());
    }

    void LoopOnce()
    {
        int n = _epoller->Wait();
        epoll_events_array_ptr array = _epoller->GetEpollEvents();
        for (int i = 0; i < n; i++)
        {
            int fd = array[i].data.fd;
            uint32_t event = array[i].events;

            // 假如epoll模型获取事件出现错误，转化为读写错误
            if (event & EPOLLERR || event & EPOLLHUP)
            {
                event |= (EPOLLIN | EPOLLOUT);
            }

            if (ExistFd(fd) && event & EPOLLIN)
            {
                _connections[fd]->GetReader()(_connections[fd]);
            }
            if (ExistFd(fd) && event & EPOLLOUT)
            {
                // std::cout << "写事件关心就绪" <<std::endl;
                _connections[fd]->GetWriter()(_connections[fd]);
            }
        }
    }

private:
    void ServerHandler(TcpConnectPtr conn)
    {
        std::string &inbuffer = conn->GetInbuffer();
        std::string message;
        // 解析获得一个完整的独立的请求报文
        for (auto ch : inbuffer)
        {
            std::cout << ch;
        }
        std::cout << std::endl;
        try
        {
            while (Unpackage(inbuffer, &message))
            {
                // std::cout << "Package: " << msg << std::endl;
                // inbuffer.erase(0, msg.size());

                ELOG("收到的报文：%s", message.c_str());
                // 对获取到的请求报文进行json的解析
                ProtocolPtr ptr = ProtocolFactory::createProtocolFromJson(message);

                if (ptr == nullptr || callbacks.find(ptr->getType()) == callbacks.end())
                {
                    unknown_callback(conn);
                    Exceptor(conn);
                }
                else
                {
                    callbacks[ptr->getType()]->onMessage(conn, ptr);
                }
            }
        }
        catch (const std::exception &e)
        {
            ELOG("处理到未知请求，连接关闭");
            Exceptor(conn);
        }
    }

    void Accept(TcpConnectPtr conn)
    {
        // DLOG("新连接到来");

        errno = 0;
        while (true)
        {
            struct sockaddr_in clientaddr;
            socklen_t len = sizeof(clientaddr);
            int newsock = accept(conn->GetFd(), (struct sockaddr *)(&clientaddr), &len);
            if (newsock > 0)
            {
                // 新连接到来
                // 将新到来的文件描述符设置为非阻塞
                SetNonBlock(newsock);
                // 创建普通连接对象
                TcpConnectPtr newconn = Factory::BuildNormalConn(newsock, EPOLLIN | EPOLLET,
                                                                 std::bind(&InetworkModule::Reader, this, std::placeholders::_1),
                                                                 std::bind(&InetworkModule::Writer, this, std::placeholders::_1),
                                                                 std::bind(&InetworkModule::Exceptor, this, std::placeholders::_1), this);

                conn_callback(newconn);

                // 将创建好的连接对象放入Reactor中
                AddConnection(newconn, EPOLLIN | EPOLLET);
            }
            else
            {
                if (newsock == 0)
                {
                    if (errno == EINTR)
                        continue;
                    else if (errno == EAGAIN)
                        break;
                }
                else
                {
                    break;
                }
            }

            // ELOG("循环进行中");
        }

        // ELOG("循环结束");
    }

    void Reader(TcpConnectPtr conn)
    {
        // DLOG("%d号文件描述符读事件就绪\n", conn->GetFd());
        // std::cout << "普通文件描述符读事件就绪" << std::endl;
        errno = 0;
        //

        // std::cout << "fd: " << conn->GetFd() << std::endl;
        while (true)
        {
            char buffer[102400];
            ssize_t n = recv(conn->GetFd(), buffer, sizeof(buffer) - 1, 0);

            if (n > 0)
            {
                buffer[n] = 0;
                conn->GetInbuffer() += buffer;
            }
            else if (n == 0)
            {
                if (!conn->GetInbuffer().empty())
                {
                    ServerHandler(conn);
                }
                // std::cout << "对端关闭连接" << std::endl;
                // 调用用户设定的连接处理函数
                disconn_callback(conn);
                Exceptor(conn);
                return;
            }
            else
            {
                if (errno == EAGAIN)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {

                    Exceptor(conn);
                    return;
                }
            }
        }

        // 当读取完数据之后，就开始处理数据
        ServerHandler(conn);
    }

    void Writer(TcpConnectPtr conn)
    {
        // DLOG("%d号文件描述符写事件就绪\n", conn->GetFd());
        // std::cout << "普通文件描述符写事件就绪" << std::endl;
        errno = 0;
        //
        // std::string &outbuffer = conn->GetOutbuffer();
        while (true)
        {
            ssize_t n = send(conn->GetFd(), conn->_outbuffer.data(), conn->_outbuffer.size(), 0);
            if (n >= 0)
            {
                // std::cout << "outbuffer: " << conn->_outbuffer << std::endl;
                conn->_outbuffer.erase(conn->_outbuffer.begin(), conn->_outbuffer.begin() + n);
                // std::cout << "outbufferoutbuffer: " << n << " " <<  conn->_outbuffer << std::endl;
                if (conn->OutEmpty())
                    break;
            }
            else
            {
                if (errno == EAGAIN)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    Exceptor(conn);
                    return;
                }
            }
        }

        // 从这里出来之后，有两种情况：
        // 一种是发送缓冲区不为空，这意味着，发送条件不就绪，这时候我们就需要epoll模型对该文件描述符的写事件关心
        if (!conn->OutEmpty())
        {
            ModConnection(conn, true, true);
        }
        // 另一种是发送缓冲区为空，这时候该文件描述符写事件仍被epoll模型关心，为了做到按需关心，我们应该从epoll模型中取消对该文件描述符的写事件关心
        else
        {
            ModConnection(conn, true, false);
        }
    }

    void Exceptor(TcpConnectPtr conn)
    {
        // DLOG("%d号文件描述符被关闭", conn->GetFd());

        // 进入异常处理之后我们的处理方式只能是关闭文件描述符，而关闭文件描述符，需要做以下事情：

        // 从epoll模型中删除该文件描述符
        // 从connections中删除该连接对象
        DelConnection(conn);

        // 关闭文件描述符
        // DLOG("关闭文件描述符");
        close(conn->GetFd());
        // DLOG("成功关闭文件描述符");
    }

    bool Unpackage(std::string &inbuffer, std::string *result)
    {
        // ELOG("接收缓冲区中的数据：%s", inbuffer.c_str());
        // 先取固定五字节的六十二位编码的数字
        if (inbuffer.size() < 5)
            return false;
        std::string stringlen = inbuffer.substr(0, 5);
        // 取到长度字符串之后对报文进行解密
        Secure::decrypt(stringlen, &stringlen);
        uint64_t len = Secure::base62ToInt(stringlen);
        DLOG("报文长度: %ld, 输入缓冲区长度: %ld", len + 5, inbuffer.size());
        if(len + 5 > inbuffer.size())
        {
            DLOG("报文长度未达到解析长度, 报文长度: %ld, 输入缓冲区长度: %ld", len + 5, inbuffer.size());
            return false;
        }

        std::string ret = inbuffer.substr(5, len);
        Secure::decrypt(ret, &ret);

        inbuffer.erase(0, len + 5);

        *result = ret;
        return true;
    }

    void Package(const std::string &msg, std::string *res)
    {
        std::string ret;
        // 将字符串长度转换为六十二编码的数字
        std::string stringlen = Secure::intToBase62(msg.size());
        ret = stringlen + msg;

        // 对报文进行简单的加密
        Secure::encryption(ret, &ret);
        *res = ret;
    }

private:
    connectedCallback conn_callback;
    disconnectedCallback disconn_callback;
    unknownCallback unknown_callback;
    std::unordered_map<Protocol::Type, std::shared_ptr<Callback>> callbacks;

private:
    bool _running;

    std::unique_ptr<Epoller> _epoller;

    std::unordered_map<int, TcpConnectPtr> _connections;
};